package PIPL::Class::BaseTest;


package PIPL::Class::BaseTest::Package1;

use base 'PIPL::Class::Base';

use constant FIELDS => {
    'field1' => { is => 'rw', default => 'default1' },
    'field2' => { is => 'ro' },
};

1;


package PIPL::Class::BaseTest::Package2;

use base 'PIPL::Class::BaseTest::Package1';

use constant FIELDS => {
    %{ PIPL::Class::BaseTest::Package1->FIELDS },
    'field2' => { is => 'rw' },
    'field3' => { is => 'ro', default => 'default3' },
};

1;


package PIPL::Class::BaseTest::Package3;

use base 'PIPL::Class::Base';

use constant FIELDS => {
    'strong_ref' => { is => 'rw' },
    'weak_ref' =>   { is => 'rw', weak_ref => 1 },
};

1;


package PIPL::Class::BaseTest;

use strict;

use base 'Test::Unit::TestCase';

sub new {
    my $self = shift()->SUPER::new(@_);
    return $self;
}

sub test_PIPL_Class_Base____isa {
    my $self = shift;

    my $obj1 = PIPL::Class::BaseTest::Package1->new;
    $self->assert_not_null($obj1);
    $self->assert($obj1->isa('PIPL::Class::Base'));

    my $obj2 = PIPL::Class::BaseTest::Package2->new;
    $self->assert_not_null($obj2);
    $self->assert($obj2->isa('PIPL::Class::Base'));

    my $obj3 = PIPL::Class::BaseTest::Package3->new;
    $self->assert_not_null($obj3);
    $self->assert($obj3->isa('PIPL::Class::Base'));
}

sub test_PIPL_Class_Base_new {
    my $self = shift;

    my $obj1 = PIPL::Class::BaseTest::Package1->new(field1 => 1, field2 => 2, field3 => 3, field4 => 4);
    $self->assert_not_null($obj1);
    $self->assert($obj1->isa('PIPL::Class::Base'));
    $self->assert($obj1->isa('PIPL::Class::BaseTest::Package1'));
    $self->assert_equals('1', $obj1->{field1});
    $self->assert_null($obj1->{properties}->{field1});
    $self->assert_equals('default1', $obj1->{defaults}->{field1});
    $self->assert_null($obj1->{field2});
    $self->assert_equals('2', $obj1->{properties}->{field2});
    $self->assert_null($obj1->{defaults}->{field2});
    $self->assert_null($obj1->{field3});
    $self->assert_equals('3', $obj1->{properties}->{field3});
    $self->assert_null($obj1->{defaults}->{field3});
    $self->assert_null($obj1->{field4});
    $self->assert_equals('4', $obj1->{properties}->{field4});
    $self->assert_null($obj1->{defaults}->{field4});

    my $obj2 = PIPL::Class::BaseTest::Package2->new(field1 => 1, field2 => 2, field3 => 3, field4 => 4);
    $self->assert_not_null($obj2);
    $self->assert($obj2->isa('PIPL::Class::Base'));
    $self->assert($obj2->isa('PIPL::Class::BaseTest::Package1'));
    $self->assert($obj2->isa('PIPL::Class::BaseTest::Package2'));
    $self->assert_equals('1', $obj2->{field1});
    $self->assert_null($obj2->{properties}->{field1});
    $self->assert_equals('default1', $obj2->{defaults}->{field1});
    $self->assert_equals('2', $obj2->{field2});
    $self->assert_null($obj2->{properties}->{field2});
    $self->assert_null($obj2->{defaults}->{field2});
    $self->assert_not_null($obj2->{field3});
    $self->assert_equals('default3', $obj2->{field3});
    $self->assert_equals('3', $obj2->{properties}->{field3});
    $self->assert_equals('default3', $obj2->{defaults}->{field3});
    $self->assert_null($obj2->{field4});
    $self->assert_equals('4', $obj2->{properties}->{field4});
    $self->assert_null($obj2->{defaults}->{field4});

    my $obj3 = $obj1->new;
    $self->assert_not_null($obj3);
    $self->assert($obj3->isa('PIPL::Class::Base'));
    $self->assert($obj3->isa('PIPL::Class::BaseTest::Package1'));
    $self->assert_equals('default1', $obj3->{field1});
    $self->assert_equals('default1', $obj3->{defaults}->{field1});
    $self->assert_null($obj3->{field2});
    $self->assert_null($obj3->{field3});

    my $obj4 = $obj2->new;
    $self->assert_not_null($obj4);
    $self->assert($obj4->isa('PIPL::Class::Base'));
    $self->assert($obj4->isa('PIPL::Class::BaseTest::Package1'));
    $self->assert_equals('default1', $obj4->{field1});
    $self->assert_equals('default1', $obj4->{defaults}->{field1});
    $self->assert_null($obj4->{field2});
    $self->assert_equals('default3', $obj4->{field3});
}

sub test_PIPL_Class_Base_new_weak_ref {
    my $self = shift;

    my $ref1 = [ 1 ];
    my $ref2 = [ 1, 2 ];
    $self->assert_equals(1, scalar @$ref1);
    $self->assert_equals(2, scalar @$ref2);

    my $obj = PIPL::Class::BaseTest::Package3->new(strong_ref => $ref1, weak_ref => $ref2);
    $self->assert_not_null($obj);
    $self->assert($obj->isa('PIPL::Class::BaseTest::Package3'));
    $self->assert_not_null($obj->{strong_ref});
    $self->assert_not_null($obj->{weak_ref});
    $self->assert_equals(1, scalar @{$obj->{strong_ref}});
    $self->assert_equals(2, scalar @{$obj->{weak_ref}});

    undef $ref1;
    undef $ref2;
    $self->assert_not_null($obj->{strong_ref});
    $self->assert_null($obj->{weak_ref});    
    $self->assert_equals(1, scalar @{$obj->{strong_ref}});
}

sub test_PIPL_Class_Base_clone_new {
    my $self = shift;

    my $obj1 = PIPL::Class::BaseTest::Package1->clone;
    $self->assert_not_null($obj1);
    $self->assert($obj1->isa('PIPL::Class::Base'));

    my $obj2 = PIPL::Class::BaseTest::Package2->clone;
    $self->assert_not_null($obj2);
    $self->assert($obj2->isa('PIPL::Class::Base'));
}

sub test_PIPL_Class_Base_clone_object {
    my $self = shift;

    my $obj1 = PIPL::Class::BaseTest::Package1->clone;
    $self->assert_not_null($obj1);
    $self->assert($obj1->isa('PIPL::Class::Base'));
    $self->assert($obj1->isa('PIPL::Class::BaseTest::Package1'));
    $obj1->{field1} = 1;
    $obj1->{field2} = [1, 2, {3, 4, 5, [6, {7, 8}, 9]}];
    $self->assert_equals(1, $obj1->{field1});
    $self->assert_deep_equals([1, 2, {3, 4, 5, [6, {7, 8}, 9]}], $obj1->{field2});
    
    my $obj2 = $obj1->clone;
    $self->assert_not_null($obj2);
    $self->assert($obj2->isa('PIPL::Class::Base'));
    $self->assert($obj2->isa('PIPL::Class::BaseTest::Package1'));
    $self->assert_equals(1, $obj2->{field1});
    $self->assert_deep_equals([1, 2, {3, 4, 5, [6, {7, 8}, 9]}], $obj2->{field2});
    $obj2->{field1} = 2;
    $obj2->{field2} = {1, 2, 3, [4, 5, {6, [7, 8, 9]}]};
    $self->assert_equals(2, $obj2->{field1});
    $self->assert_equals(1, $obj1->{field1});
    $self->assert_deep_equals({1, 2, 3, [4, 5, {6, [7, 8, 9]}]}, $obj2->{field2});
    $self->assert_deep_equals([1, 2, {3, 4, 5, [6, {7, 8}, 9]}], $obj1->{field2});
}

sub test_PIPL_Class_Base_clone_reference {
    my $self = shift;

    my $ref1 = [1, 2, 3, 4];
    my $ref2 = PIPL::Class::Base->clone($ref1);
    $self->assert_not_equals($ref1, $ref2);
    $self->assert_deep_equals($ref1, $ref2);

    my $ref3 = {1, 2, 3, 4};
    my $ref4 = PIPL::Class::Base->clone($ref3);
    $self->assert_not_equals($ref3, $ref4);
    $self->assert_deep_equals($ref3, $ref4);

    my $ref5 = [1, 2, {3, 4, 5, [6, {7, 8}, 9]}];
    my $ref6 = PIPL::Class::Base->clone($ref5);
    $self->assert_not_equals($ref5, $ref6);
    $self->assert_deep_equals($ref5, $ref6);
}

1;
